package com.atguigu.gulimall.search.service.impl;

import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.to.es.SkuEsModel;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.search.config.GulimallElasticSearchConfig;
import com.atguigu.gulimall.search.constant.EsConstant;
import com.atguigu.gulimall.search.feign.ProductFeignService;
import com.atguigu.gulimall.search.vo.AttrResponseVo;
import com.atguigu.gulimall.search.vo.BrandVo;
import com.atguigu.gulimall.search.vo.SearchParams;
import com.atguigu.gulimall.search.vo.SearchResult;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.atguigu.gulimall.search.service.MallSearchService;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Service("SearchService")
public class MallSearchServiceImpl implements MallSearchService {

    @Autowired
    RestHighLevelClient restHighLevelClient;//去es检索请求


    @Autowired
    private ProductFeignService productFeignService;

    @Override
    public SearchResult search(SearchParams params) {
        //1、动态构建出查询需要的DSL语句

        //1、准备请求
        SearchRequest searchRequest = buildSearchRequest(params);

        SearchResult searchResult=null;

        //2、执行检索请求
        try {
            SearchResponse search = restHighLevelClient.search(searchRequest, GulimallElasticSearchConfig.Common_OPTIONS);

            searchResult = buildSearchResponse(search,params);
            System.out.println(search);
            //3、分析响应数据 封装我们想要的格式
        } catch (IOException e) {
            e.printStackTrace();
        }


        return searchResult;
    }

    /**
     * 响应信息
     *
     * @param search
     * @param params
     * @return
     */
    private SearchResult buildSearchResponse(SearchResponse search,SearchParams params) {
        SearchResult searchResult = new SearchResult();
        SearchHits hits = search.getHits();
        //商品信息
        SearchHit[] hits1 = hits.getHits();
        ArrayList<SkuEsModel> skuList = new ArrayList<>();
        for (SearchHit searchHit : hits1) {
            String sourceAsString = searchHit.getSourceAsString();
            SkuEsModel SkuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);//映射到SkuEsModel
            if(StringUtil.isNotEmpty(params.getKeyword())){
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                HighlightField skuTitle = highlightFields.get("skuTitle");
                SkuEsModel.setSkuTitle(skuTitle.getFragments()[0].toString());
            }
            skuList.add(SkuEsModel);
        }
        searchResult.setProducts(skuList);
        //分页信息
        Long total = hits.getTotalHits().value;
        searchResult.setTotal(total);
        searchResult.setPageNum(params.getPageNum());
        int totalPage = total % EsConstant.PRODUCT_PAGE_SIZE == 0 ? (int)(total / EsConstant.PRODUCT_PAGE_SIZE) : (int)(total / EsConstant.PRODUCT_PAGE_SIZE + 1);
        searchResult.setTotalPages(totalPage);
        ArrayList<Integer> pav = new ArrayList<>();
        for (int i = 1; i <= totalPage; i++) {
            pav.add(i);
        }
        searchResult.setPageNas(pav);

        Aggregations aggregations = search.getAggregations();
        //品牌信息
        Terms brand_agg = aggregations.get("brand_agg");
        List<? extends Terms.Bucket> buckets = brand_agg.getBuckets();
        ArrayList<SearchResult.BrandVo> brandVos = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            SearchResult.BrandVo brandVo = new SearchResult.BrandVo();
            //品牌ID
            brandVo.setBrandId(bucket.getKeyAsNumber().longValue());
            Aggregations aggregations1 = bucket.getAggregations();
            Terms brand_img_agg = aggregations1.get("brand_img_agg");
            List<? extends Terms.Bucket> buckets1 = brand_img_agg.getBuckets();
            brandVo.setBrandImg(buckets1.get(0).getKeyAsString());// 品牌图片
            Terms brand_name_agg = aggregations1.get("brand_name_agg");
            List<? extends Terms.Bucket> buckets2 = brand_name_agg.getBuckets();
            brandVo.setBrandName(buckets2.get(0).getKeyAsString());//品牌名称
            brandVos.add(brandVo);
        }
        searchResult.setBrands(brandVos);
        //分类信息
        ParsedLongTerms category_log_agg = aggregations.get("category_log_agg");
        List<? extends Terms.Bucket> buckets1 = category_log_agg.getBuckets();
        ArrayList<SearchResult.CategoryVo> categorys = new ArrayList<>();
        for (Terms.Bucket bucket : buckets1) {
            SearchResult.CategoryVo categoryVo = new SearchResult.CategoryVo();
            categoryVo.setCatelogId(Long.parseLong(bucket.getKeyAsString()));
            Aggregations aggregations1 = bucket.getAggregations();
            ParsedStringTerms catelog_name_agg = aggregations1.get("catelog_name_agg");
            categoryVo.setCatelogName(catelog_name_agg.getBuckets().get(0).getKeyAsString());
            categorys.add(categoryVo);
        }
        searchResult.setCategorys(categorys);
        //属性信息
        ParsedNested attr_agg = aggregations.get("attr_agg");
        Aggregations aggregations1 = attr_agg.getAggregations();
        ParsedLongTerms attr_id_agg = aggregations1.get("attr_id_agg");
        List<? extends Terms.Bucket> buckets2 = attr_id_agg.getBuckets();
        ArrayList<SearchResult.AttrVo> attrVos = new ArrayList<>();
        for (Terms.Bucket bucket : buckets2) {
            SearchResult.AttrVo attrVo = new SearchResult.AttrVo();
            attrVo.setAttrId(bucket.getKeyAsNumber().longValue());
            Aggregations aggregations2 = bucket.getAggregations();
            ParsedStringTerms attr_name_agg = aggregations2.get("attr_name_agg");
            List<? extends Terms.Bucket> buckets3 = attr_name_agg.getBuckets();
            attrVo.setAttrName(buckets3.get(0).getKeyAsString());

            ParsedStringTerms attr_value_agg = aggregations2.get("attr_value_agg");
            List<? extends Terms.Bucket> buckets4 = attr_value_agg.getBuckets();
            ArrayList<String> attrV = new ArrayList<>();
            for (Terms.Bucket bucket1 : buckets4) {
                attrV.add(bucket1.getKeyAsString());
            }
            attrVo.setAttrValue(attrV);
            attrVos.add(attrVo);
        }
        searchResult.setAttrs(attrVos);
        //6、构建面包屑导航功能  属性相关
        if (params.getAttr() !=null && params.getAttr().size()>0){
            List<SearchResult.NavVo> collect = params.getAttr().stream().map(attr -> {
                //1、分析每个attrs传递过来的参数值
                SearchResult.NavVo navVo = new SearchResult.NavVo();
                String[] s = attr.split("_");
                navVo.setNavValue(s[1]);
                R r = productFeignService.attrInfo(Long.parseLong(s[0]));
                searchResult.getAttrIds().add(Long.parseLong(s[0]));
                if (r.getCode() == 0){
                    AttrResponseVo attrResponseVo = (AttrResponseVo) r.getData( new TypeReference<AttrResponseVo>() {
                    });
                    if(attrResponseVo != null){
                        navVo.setNavName(attrResponseVo.getAttrName());
                    }
                }else{
                    navVo.setNavName(s[0]);
                }
                //2、取消了这个面包屑之后，跳转的地方，将请求的地址的url里面的当前条件置空
                //拿到所有的查询条件去掉当前
                String replace = replaceQueryString(params, attr,"attrs");
                navVo.setLink("http://search.gulimall.com/list.html?"+replace);

                return navVo;
            }).collect(Collectors.toList());
            searchResult.setNavs(collect);
        }
        //品牌相关
        if (params.getBrandId() != null && params.getBrandId().size() >0){
            List<SearchResult.NavVo> navs = searchResult.getNavs();
            SearchResult.NavVo navVo = new SearchResult.NavVo();
            navVo.setNavName("品牌");
            //远程查询所有品牌
            R r = productFeignService.brandsInfo(params.getBrandId());
            if (r.getCode() == 0){
                List<BrandVo> brand = (List<BrandVo>) r.getData(new TypeReference<List<BrandVo>>() {
                });
                StringBuffer buffer = new StringBuffer();
                String replace = "";
                if(brand!=null){
                    for (BrandVo brandVo : brand) {
                        buffer.append(brandVo.getBrandName()+";");
                        replace = replaceQueryString(params, brandVo.getBrandId()+"","brandId");
                    }
                }
                navVo.setNavValue(buffer.toString());
                navVo.setLink("http://search.gulimall.com/list.html?"+replace);
            }
            navs.add(navVo);
            searchResult.setNavs(navs);

        }

        return searchResult;
    }

    private String replaceQueryString(SearchParams param, String value, String key) {
        String encode = null;
        try {
            encode = URLEncoder.encode(value, "UTF-8");
            encode = encode.replace("+", "%20");//浏览器和java对+号的差异化处理
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        String replace = param.get_queryString()!=null?param.get_queryString().replace("&"+key+"=" + encode, ""):"";
        return replace;
    }


    /**
     * 构建DSL查询  组装参数向es请求数据
     *
     * @param params
     * @return
     */
    private SearchRequest buildSearchRequest(SearchParams params) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//构建DSL语句
        /**
         * 查询 过滤（按照属性，品牌，分类，价格区间，库存）
         */
        //构建bool -query  bool
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //查询商品名称 --- must
        if(StringUtil.isNotEmpty(params.getKeyword())){
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle",params.getKeyword()));
        }
        //查询分类---filter  term
        if(params.getCatalog3Id() != null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId",params.getCatalog3Id()));
        }
        //品牌ID ---- filter term
        if(params.getBrandId() != null && params.getBrandId().size()>0){
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId",params.getBrandId()));
        }
        //是否有存库
        boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock",params.getHasStock()==1));
        //查询价格区间
        if(StringUtil.isNotEmpty(params.getSkuPrice())){
            /**
             * {
             *           "range": {
             *             "skuPrice": {
             *               "gte": 0,
             *               "lte": 6000
             *             }
             *           }
             *         }
             * 0_500/_500/10_
             */
            String[] s = params.getSkuPrice().split("_");
            RangeQueryBuilder skuPrice = QueryBuilders.rangeQuery("skuPrice");
            if(s.length==2){
                skuPrice.lte(s[0]).gte(s[1]);
            }else if(s.length==1 && params.getSkuPrice().startsWith("_")){
                skuPrice.lte(s[0]);
            }else if(s.length==1 && params.getSkuPrice().endsWith("_")){
                skuPrice.gte(s[0]);
            }

            boolQueryBuilder.filter(skuPrice);
        }
        //nested 属性查询
        if(params.getAttr()!=null && params.getAttr().size()>0){
            //页面属性值attr=1_5寸:8寸&attr=2_8G:16G
            List<String> attrs = params.getAttr();
            for (String attr : attrs) {
                /**
                 * "nested": {
                 *             "path": "attrs",
                 *             "query": {
                 *               "bool": {
                 *                 "must": [
                 *                   {
                 *                     "terms": {
                 *                       "attrs.attrId": [
                 *                         "9",
                 *                         "13",
                 *                         "14"
                 *                       ]
                 *                     }
                 *                   },
                 *                   {
                 *                     "term": {
                 *                       "attrs.attrValue": {
                 *                         "value": "2019"
                 *                       }
                 *                     }
                 *                   }
                 *                 ]
                 *               }
                 *             }
                 *           }
                 */
                BoolQueryBuilder nestedBoolQuery = QueryBuilders.boolQuery();//构建nested查询
                String[] s = attr.split("_");
                String attrId = s[0];
                nestedBoolQuery.must(QueryBuilders.termQuery("attrs.attrId",attrId));
                String[] attrValues = s[1].split(":");
                nestedBoolQuery.must(QueryBuilders.termsQuery("attrs.attrValue",attrValues));
                //每一个必须都得生成一个nested查询
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", nestedBoolQuery, ScoreMode.None);
                boolQueryBuilder.filter(nestedQuery);//这里是list add可以接收多个
            }
        }
        sourceBuilder.query(boolQueryBuilder);//执行查询操作
        /**
         *  排序 分页 高亮
         */
        //排序sort=skuPrice_desc/asc
        if(StringUtil.isNotEmpty(params.getSort())){
            String sort = params.getSort();
            String[] s = sort.split("_");
            SortOrder order = s[1].equalsIgnoreCase("desc") ? SortOrder.DESC : SortOrder.ASC;
            sourceBuilder.sort(s[0], order);
        }
        //分页
        sourceBuilder.from((params.getPageNum()-1)*EsConstant.PRODUCT_PAGE_SIZE);
        sourceBuilder.size(EsConstant.PRODUCT_PAGE_SIZE);
        //高亮
        if(StringUtil.isNotEmpty(params.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }
        /**
         * 聚合分析 品牌、分类、属性
         */
        //1 聚合品牌
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brand_agg");
        brandAgg.field("brandId").size(50);
        brandAgg.subAggregation(AggregationBuilders.terms("brand_name_agg").field("brandName").size(1));
        brandAgg.subAggregation(AggregationBuilders.terms("brand_img_agg").field("brandImg").size(1));
        sourceBuilder.aggregation(brandAgg);
        //2、聚合分类
        TermsAggregationBuilder category_log_agg = AggregationBuilders.terms("category_log_agg");
        category_log_agg.field("catalogId").size(50);
        category_log_agg.subAggregation(AggregationBuilders.terms("catelog_name_agg").field("catalogName").size(1));
        sourceBuilder.aggregation(category_log_agg);
        //3、nested 属性聚合
        /**
         * "attr_agg": {
         *       "nested": {
         *         "path": "attrs"
         *       },
         *       "aggs": {
         *         "attr_id_agg": {
         *           "terms": {
         *             "field": "attrs.attrId",
         *             "size": 10
         *           },
         *           "aggs": {
         *             "attr_name_agg": {
         *               "terms": {
         *                 "field": "attrs.attrName",
         *                 "size": 10
         *               }
         *             },
         *             "attr_value_agg": {
         *               "terms": {
         *                 "field": "attrs.attrValue",
         *                 "size": 10
         *               }
         *             }
         *           }
         *         }
         *       }
         *     }
         */
        NestedAggregationBuilder attr_agg = AggregationBuilders.nested("attr_agg", "attrs");
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attr_id_agg").field("attrs.attrId").size(1);
        attrIdAgg.subAggregation(AggregationBuilders.terms("attr_name_agg").field("attrs.attrName").size(1));//子聚合
        attrIdAgg.subAggregation(AggregationBuilders.terms("attr_value_agg").field("attrs.attrValue").size(50));//子聚合
        attr_agg.subAggregation(attrIdAgg);//大聚合attr_agg

        sourceBuilder.aggregation(attr_agg);

        System.out.println(sourceBuilder.toString());

        SearchRequest searchRequest = new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX},sourceBuilder);

        return searchRequest;
    }
}
